最近在写一个google / facebook oauth登录系统,前端使用react,后端使用nodejs + express,前后端分别部署在vercel和heroku上,不可避免的遇到很多跨域问题。

今天在将本地代码deploy到生产环境的时候,发现google oauth虽然能成功调用callback url重定向到后端,后端也能设置cookie,但后续的api操作却没有带上之前返回的cookie,导致无法通过api获取用户数据等信息。然而之前在开发环境测试的时候,api操作确实带上了cookie并成功获取数据,并且:

  1. 在开发和生产环境中后端都设置了cors({ credentials: true, origin: process.env.CLIENT_BASE_URL })
  2. 前端axios获取api数据时也设置了axios.defaults.withCredentials = true;
  3. 生产环境中前端地址localhost:3000和后端地址localhost:5000同属于跨域,但测试正常

查看console,发现以下错误信息:

1
2
3
4
5
6
7
A cookie associated with a cross-site resource at http://MYAPI.URL was set
without the `SameSite` attribute. A future release of Chrome will only deliver
cookies with cross-site requests if they are set with `SameSite=None` and
`Secure`. You can review cookies in developer tools under
Application>Storage>Cookies and see more details at
https://www.chromestatus.com/feature/5088147346030592 and
https://www.chromestatus.com/feature/5633521622188032.

搜索后发现,该错误是由于chrome等浏览器最近的安全升级导致,该升级要求所有跨域cookie操作都必须要对cookie设置为samesite="none"secure="true",而且该升级只针对于host不同的情况,其余跨域诸如端口号等不受到影响,这也解释了为什么开发环境测试通过而生产环境出现错误。 MDN参考

后续设置express-session

1
2
3
4
5
6
7
8
9
10
const sessionConfig = {
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: {
sameSite: "none",
secure: true
},
};
app.use(session(sessionConfig));

修改后新问题又来了,这次后端直接不设置cookie了,即response中不包含set-cookie。查阅文档后发现当设置cookie为secure时,需要通过https设置cookie,使用http将默认不设置cookie。因此express加多一行代码

1
app.set('trust proxy', 1) // trust first proxy

这次在生产环境测试成功,但生产环境的前后端地址都是由第三方提供的https地址,不知道为何也会出现这样的问题,猜想可能需要自行配置有效证书。

Update on 2020-09-21

上述问题其实是由于苹果safari浏览器的最新安全策略所导致的,现时safari浏览器无论是否设置cookie为secure或使用https,都仅允许同一域名下的跨域异步操作,在Google Chrome和Firefox浏览器测试中不存在上述问题。将server (https://shop-api.wei.ai) 和client (https://shop.wei.ai) 部署到同一域名下后问题解决。